{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Operations on (key,val) RDDs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Types of spark operations\n",
    "\n",
    "There are Three types of operations on RDDs: Transformations, Actions and Shuffles.\n",
    "\n",
    "* The most expensive operations are those the require communication between nodes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "**Transformations:** RDD $\\to$ RDD.\n",
    "  * **Examples** map, filter, sample, [More](http://spark.apache.org/docs/latest/rdd-programming-guide.html#transformations)\n",
    "  * **No** communication needed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "**Actions:** RDD $\\to$ Python-object in head node.\n",
    "  * **Examples:** reduce, collect, count, take, [More](http://spark.apache.org/docs/latest/rdd-programming-guide.html#actions)\n",
    "  * **Some** communication needed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "**Shuffles:** RDD $\\to$ RDD, **shuffle** needed\n",
    "  * **Examples:** sort, distinct, repartition, sortByKey, reduceByKey, join [More](http://spark.apache.org/docs/latest/rdd-programming-guide.html#shuffle-operations)\n",
    "  * **A LOT** of communication might be needed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "   \n",
    "   \n",
    "   \n",
    "   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "### Key/value pairs\n",
    "\n",
    "* A python dictionary is a collection of *key/value* pairs.\n",
    "* The **key** is used to find a set of pairs with the particular key.\n",
    "* The **value** can be anything.\n",
    "* Spark has a set of special operations for *(key,value)* RDDs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "Spark provides specific functions to deal with RDDs in which each element is a key/value pair. Key/value RDDs expose new operations (e.g. aggregating and grouping together data with the same key and grouping together two different RDDs.) Such RDDs are also called pair RDDs. **In python, each element of a pair RDD is a pair tuple.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true,
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "#start the SparkContext\n",
    "import findspark\n",
    "findspark.init()\n",
    "\n",
    "from pyspark import SparkContext\n",
    "sc = SparkContext(master=\"local[4]\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "### Creating (key,value) RDDS\n",
    "\n",
    "**Method 1:** `parallelize` a list of pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(1, 2), (3, 4)]\n"
     ]
    }
   ],
   "source": [
    "pair_rdd = sc.parallelize([(1,2), (3,4)])\n",
    "print(pair_rdd.collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "**Method 2:** `map` a function that maps elements to key/value pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(1, 1), (2, 4), (3, 9), (4, 16), (2, 4), (5, 25), (6, 36)]\n"
     ]
    }
   ],
   "source": [
    "regular_rdd = sc.parallelize([1, 2, 3, 4, 2, 5, 6])\n",
    "pair_rdd = regular_rdd.map( lambda x: (x, x*x) )\n",
    "print(pair_rdd.collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Transformations on (key,value) rdds"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### reduceByKey(func)\n",
    "Apply the reduce function on the values with the same key. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original RDD : [(1, 2), (2, 4), (2, 6)]\n",
      "After transformation :  [(1, 2), (2, 10)]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(1,2), (2,4), (2,6)])\n",
    "print(\"Original RDD :\", rdd.collect())\n",
    "print(\"After transformation : \", rdd.reduceByKey(lambda a,b: a+b).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "Note that although it is similar to the reduce function, it is implemented as a transformation and not as an action because the dataset can have very large number of keys. So, it does not return values to the driver program. Instead, it returns a new RDD. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### sortByKey(): \n",
    "Sort RDD by keys in ascending order. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original RDD : [(2, 2), (1, 4), (3, 6)]\n",
      "After transformation :  [(1, 4), (2, 2), (3, 6)]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(2,2), (1,4), (3,6)])\n",
    "print(\"Original RDD :\", rdd.collect())\n",
    "print(\"After transformation : \", rdd.sortByKey().collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "**Note:** The output of sortByKey() is an RDD. This means that  RDDs do have a meaningful order, which extends between partitions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### mapValues(func):\n",
    "Apply func to each value of RDD without changing the key. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original RDD : [(1, 2), (2, 4), (2, 6)]\n",
      "After transformation :  [(1, 4), (2, 8), (2, 12)]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(1,2), (2,4), (2,6)])\n",
    "print(\"Original RDD :\", rdd.collect())\n",
    "print(\"After transformation : \", rdd.mapValues(lambda x: x*2).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### groupByKey(): \n",
    "Returns a new RDD of `(key,<iterator>)` pairs where the iterator iterates over the values associated with the key."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "[Iterators](http://anandology.com/python-practice-book/iterators.html) are python objects that generate a sequence of values. Writing a loop over `n` elements as \n",
    "```python\n",
    "for i in range(n):\n",
    "    ##do something\n",
    "```\n",
    "is inefficient because it first allocates a list of `n` elements and then iterates over it.\n",
    "Using the iterator `xrange(n)` achieves the same result without materializing the list. Instead, elements are generated on the fly.\n",
    "\n",
    "To materialize the list of values returned by an iterator we will use the list comprehension command:\n",
    "```python\n",
    "[a for a in <iterator>]\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original RDD : [(1, 2), (2, 4), (2, 6)]\n",
      "After transformation :  [(1, [2]), (2, [4, 6])]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(1,2), (2,4), (2,6)])\n",
    "print(\"Original RDD :\", rdd.collect())\n",
    "print(\"After transformation : \", rdd.groupByKey().mapValues(lambda x:[a for a in x]).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "####  flatMapValues(func): \n",
    "\n",
    "Similar to `flatMap()`: creates a separate key/value pair for each element of the list generated by the map operation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "`func` is a function that takes as input a single value and returns an iterator that generates a sequence of values.\n",
    "The application of flatMapValues operates on a key/value RDD. It applies `func` to each value, and gets an list (generated by the iterator) of values. It then combines each of the values with the original key to produce a list of key-value pairs. These lists are concatenated as in `flatMap`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original RDD : [(1, 2), (2, 4), (2, 6)]\n",
      "After transformation :  [(1, 2), (1, 3), (2, 4), (2, 5), (2, 6), (2, 7)]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(1,2), (2,4), (2,6)])\n",
    "print(\"Original RDD :\", rdd.collect())\n",
    "# the lambda function generates for each number i, an iterator that produces i,i+1\n",
    "print(\"After transformation : \", rdd.flatMapValues(lambda x: list(range(x,x+2))).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "#### (Advanced)  combineByKey(createCombiner, mergeValue, mergeCombiner): \n",
    "Combine values with the same key using a different result type.\n",
    "\n",
    "This is the most general of the per-key aggregation functions. Most of the other per-key combiners are implemented using it. \n",
    "\n",
    "The elements of the original RDD are considered here *values*\n",
    "\n",
    "Values are converted into *combiners* which we will refer to here as \"accumulators\". An example of such a mapping is the mapping of the value *word* to the accumulator (*word*,1) that is done in WordCount.\n",
    "\n",
    "accumulators are then combined with values and the other combiner to generate a result for each key.\n",
    "\n",
    "For example, we can use it to calculate per-activity average durations as follows. Consider an RDD of key/value pairs where keys correspond to different activities and values correspond to duration.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('Work', (14, 3)), ('Play', (12, 3)), ('Sleep', (21, 3))]\n",
      "[('Work', 4.666666666666667), ('Play', 4.0), ('Sleep', 7.0)]\n"
     ]
    }
   ],
   "source": [
    "rdd = sc.parallelize([(\"Sleep\", 7), (\"Work\",5), (\"Play\", 3), \n",
    "                      (\"Sleep\", 6), (\"Work\",4), (\"Play\", 4),\n",
    "                      (\"Sleep\", 8), (\"Work\",5), (\"Play\", 5)])\n",
    "\n",
    "sum_counts = rdd.combineByKey(\n",
    "    (lambda x: (x, 1)), # createCombiner maps each value into a  combiner (or accumulator)\n",
    "    (lambda acc, value: (acc[0]+value, acc[1]+1)),\n",
    "#mergeValue defines how to merge a accumulator with a value (saves on mapping each value to an accumulator first)\n",
    "    (lambda acc1, acc2: (acc1[0]+acc2[0], acc1[1]+acc2[1])) # combine accumulators\n",
    ")\n",
    "\n",
    "print(sum_counts.collect())\n",
    "duration_means_by_activity = sum_counts.mapValues(lambda value:\n",
    "                                                  value[0]*1.0/value[1]) \\\n",
    "                                            .collect()\n",
    "print(duration_means_by_activity)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "To understand combineByKey(), it’s useful to think of how it handles each element it processes. As combineByKey() traverses through the elements in a partition, each element either has a key it hasn’t seen before or has the same key as a previous element.\n",
    "\n",
    "If it’s a new key, createCombiner() is called to create the initial value for the accumulator on that key. In the above example, the accumulator is a tuple initialized as (x, 1) where x is a value in original RDD. Note that createCombiner() is called only when a key is seen for the first time in **each partition.**\n",
    "\n",
    "If it is a key we have seen before while processing that partition, it will instead use the provided function, mergeValue(), with the current value for the accumulator for that key and the new value.\n",
    "\n",
    "Since each partition is processed independently, we can have multiple accumulators for the same key. When we are merging the results from each partition, if two or more partitions have an accumulator for the same key, we merge the accumulators using the user-supplied mergeCombiners() function. In the above example, we are just adding the 2 accumulators element-wise."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Transformations on two (key-value) RDDs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rdd1= [(1, 2), (2, 1), (2, 2)]\n",
      "rdd2= [(2, 5), (3, 1)]\n"
     ]
    }
   ],
   "source": [
    "rdd1 = sc.parallelize([(1,2),(2,1),(2,2)])\n",
    "rdd2 = sc.parallelize([(2,5),(3,1)])\n",
    "print('rdd1=',rdd1.collect())\n",
    "print('rdd2=',rdd2.collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 1. subtractByKey: \n",
    "Remove from RDD1 all elements whose key is present in RDD2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rdd1= [(1, 2), (2, 1), (2, 2)]\n",
      "rdd2= [(2, 5), (3, 1)]\n",
      "Result: [(1, 2)]\n"
     ]
    }
   ],
   "source": [
    "print('rdd1=',rdd1.collect())\n",
    "print('rdd2=',rdd2.collect())\n",
    "print(\"Result:\", rdd1.subtractByKey(rdd2).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### 2. join: \n",
    "* A fundamental operation in relational databases.\n",
    "* assumes two tables have a **key** column in common. \n",
    "* merges rows with the same key.\n",
    "\n",
    "Suppose we have two `(key,value)` datasets "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "|**dataset 1**|                                     |..........| **dataset 2** | \t       \t     |\n",
    "|-------------|-------------------------------------|   |-------------|-----------------|\n",
    "| **key=name**   |   **(gender,occupation,age)**    |   |  **key=name**   |   **hair color**    |\n",
    "| John   |  (male,cook,21)                          |   | Jill   |  blond |\n",
    "| Jill   |  (female,programmer,19)                  |   | Grace  |  brown |         \n",
    "| John   |  (male, kid, 2)                          |   | John   |  black |\n",
    "| Kate   |  (female, wrestler, 54)                  |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "When `Join` is called on datasets of type `(Key, V)` and `(Key, W)`, it  returns a dataset of `(Key, (V, W))` pairs with all pairs of elements for each key. Joining the 2 datasets above yields:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "|   key = name | (gender,occupation,age),haircolor |\n",
    "|--------------|-----------------------------------|\n",
    "| John         | ((male,cook,21),black)             |\n",
    "| John         | ((male, kid, 2),black)             |\n",
    "| Jill         | ((female,programmer,19),blond)     |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rdd1= [(1, 2), (2, 1), (2, 2)]\n",
      "rdd2= [(2, 5), (3, 1)]\n",
      "Result: [(2, (1, 5)), (2, (2, 5))]\n"
     ]
    }
   ],
   "source": [
    "print('rdd1=',rdd1.collect())\n",
    "print('rdd2=',rdd2.collect())\n",
    "print(\"Result:\", rdd1.join(rdd2).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "### Variants of join.\n",
    "There are four variants of `join` which differ in how they treat keys that appear in one dataset but not the other.\n",
    "* `join` is an *inner* join which means that keys that appear only in one dataset are eliminated.\n",
    "* `leftOuterJoin` keeps all keys from the left dataset even if they don't appear in the right dataset. The result of leftOuterJoin in our example will contain the keys `John, Jill, Kate`\n",
    "* `rightOuterJoin` keeps all keys from the right dataset even if they don't appear in the left dataset. The result of leftOuterJoin in our example will contain the keys `Jill, Grace, John`\n",
    "* `FullOuterJoin` keeps all keys from both datasets. The result of leftOuterJoin in our example will contain the keys `Jill, Grace, John, Kate`\n",
    "\n",
    "In outer joins, if the element appears only in one dataset, the element in `(K,(V,W))` that does not appear in the dataset is represented bye `None`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "#### 3. rightOuterJoin: \n",
    "Perform a right join between two RDDs. Every key in the right/second RDD will be present at least once."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rdd1= [(1, 2), (2, 1), (2, 2)]\n",
      "rdd2= [(2, 5), (3, 1)]\n",
      "Result: [(2, (1, 5)), (2, (2, 5)), (3, (None, 1))]\n"
     ]
    }
   ],
   "source": [
    "print('rdd1=',rdd1.collect())\n",
    "print('rdd2=',rdd2.collect())\n",
    "print(\"Result:\", rdd1.rightOuterJoin(rdd2).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "#### 4. leftOuterJoin: Perform a left join between two RDDs. Every key in the left RDD will be present at least once."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rdd1= [(1, 2), (2, 1), (2, 2)]\n",
      "rdd2= [(2, 5), (3, 1)]\n",
      "Result: [(1, (2, None)), (2, (1, 5)), (2, (2, 5))]\n"
     ]
    }
   ],
   "source": [
    "print('rdd1=',rdd1.collect())\n",
    "print('rdd2=',rdd2.collect())\n",
    "print(\"Result:\", rdd1.leftOuterJoin(rdd2).collect())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Actions on (key,val) RDDs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "rdd = sc.parallelize([(1,2), (2,4), (2,6)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 1. countByKey(): \n",
    "Count the number of elements for each key. Returns a dictionary for easy access to keys."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RDD:  [(1, 2), (2, 4), (2, 6)]\n",
      "Result: defaultdict(<class 'int'>, {1: 1, 2: 2})\n"
     ]
    }
   ],
   "source": [
    "print(\"RDD: \", rdd.collect())\n",
    "result = rdd.countByKey()\n",
    "print(\"Result:\", result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 2. collectAsMap(): \n",
    "Collect the result as a dictionary to provide easy lookup."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RDD:  [(1, 2), (2, 4), (2, 6)]\n",
      "Result: {1: 2, 2: 6}\n"
     ]
    }
   ],
   "source": [
    "print(\"RDD: \", rdd.collect())\n",
    "result = rdd.collectAsMap()\n",
    "print(\"Result:\", result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 3. lookup(key): \n",
    "Return all values associated with the provided key."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RDD:  [(1, 2), (2, 4), (2, 6)]\n",
      "Result: [4, 6]\n"
     ]
    }
   ],
   "source": [
    "print(\"RDD: \", rdd.collect())\n",
    "result = rdd.lookup(2 )\n",
    "print(\"Result:\", result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Summary\n",
    "\n",
    "* We saw some more of the operations on Pair RDDs\n",
    "* For more, see the spark [RDD programming guide]( http://spark.apache.org/docs/latest/rdd-programming-guide.html)\n",
    "* Next **DataFrames** and Spark-SQL"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Slideshow",
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {
    "height": "512px",
    "width": "252px"
   },
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": "block",
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
